/*
 * CsvImportDialog.cpp
 *
 * Copyright 2012 Pieter Agten
 *
 * This file is part of tltgen.
 *
 * tltgen is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * tltgen is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with tltgen.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file   CsvImportDialog.cpp
 * @author Pieter Agten (pieter.agten@gmail.com)
 * @date   12 feb 2013
 */

#include <wx/wx.h>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <fstream>

#include "Circuit.hpp"
#include "AnalogToDigitalConverter.hpp"
#include "CelsiusScale.hpp"
#include "FahrenheitScale.hpp"
#include "KelvinScale.hpp"
#include "CircuitPanel.hpp"
#include "CsvThermistorTableReader.hpp"

#include "CsvImportDialog.hpp"

using boost::shared_ptr;
using boost::make_shared;

namespace tltgen {

  BEGIN_EVENT_TABLE(CsvImportDialog, wxDialog)
  END_EVENT_TABLE()

  CsvImportDialog::CsvImportDialog(wxWindow* parent, wxWindowID id, const wxString& title, 
				   const wxPoint& pos, const wxSize& size, long style) :
    wxDialog(parent, id, title, pos, size, style)
  {
    initializeComponents();
  }

  CsvImportDialog::~CsvImportDialog()
  { }

  

  void CsvImportDialog::initializeComponents()
  {
    wxSizer* vbox = new wxBoxSizer(wxVERTICAL);
    wxSizer* fgs = new wxFlexGridSizer(4,2, 3,3);

    fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Skip first lines")), 0, wxALIGN_CENTER_VERTICAL);
    this->spnNbLinesToSkip = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(70, wxDefaultSize.GetHeight()));
    this->spnNbLinesToSkip->SetRange(0, 9999);
    this->spnNbLinesToSkip->SetValue(0);
    fgs->Add(this->spnNbLinesToSkip, 0, wxLEFT, 5);

    fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Separator")), 0, wxALIGN_CENTER_VERTICAL);
    wxString separatorOptions[] = {wxT("Comma"), wxT("Dot"), wxT("Space"), wxT("Tab")};
    this->comSeparator = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, WXSIZEOF(separatorOptions), separatorOptions, wxCB_DROPDOWN);
    this->comSeparator->SetValue(separatorOptions[0]);
    fgs->Add(this->comSeparator, 0, wxLEFT, 5);

    wxSizer* hbox = new wxBoxSizer(wxHORIZONTAL);
    fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Temperature column")), 0, wxALIGN_CENTER_VERTICAL);
    this->spnTemperatureColumn = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(50, wxDefaultSize.GetHeight()));
    this->spnTemperatureColumn->SetRange(0, 99);
    this->spnTemperatureColumn->SetValue(0);
    hbox->Add(this->spnTemperatureColumn, 0, wxLEFT, 5);

    hbox->Add(new wxStaticText(this, wxID_ANY, wxT("Scale")), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 10);
    wxString temperatureScaleOptions[] = {wxT("Celsius"), wxT("Fahrenheit"), wxT("Kelvin")};
    this->comTemperatureScale = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, WXSIZEOF(temperatureScaleOptions), temperatureScaleOptions, wxCB_READONLY);
    this->comTemperatureScale->SetValue(temperatureScaleOptions[0]);
    hbox->Add(this->comTemperatureScale, 0, wxLEFT, 5);
    fgs->Add(hbox);
      
    hbox = new wxBoxSizer(wxHORIZONTAL);
    fgs->Add(new wxStaticText(this, wxID_ANY, wxT("Other column")), 0, wxALIGN_CENTER_VERTICAL);
    this->spnOtherColumn = new wxSpinCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(50, wxDefaultSize.GetHeight()));
    this->spnOtherColumn->SetRange(0, 99);
    this->spnOtherColumn->SetValue(1);
    hbox->Add(this->spnOtherColumn, 0, wxLEFT, 5);

    hbox->Add(new wxStaticText(this, wxID_ANY, wxT("Type")), 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 13);
    wxString otherColumnTypeOptions[] = {wxT("Resistance"), wxT("Voltage"), wxT("ADC value")};
    this->comOtherColumnType = new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, WXSIZEOF(otherColumnTypeOptions), otherColumnTypeOptions, wxCB_READONLY);
    this->comOtherColumnType->SetValue(otherColumnTypeOptions[0]);
    hbox->Add(this->comOtherColumnType, 0, wxLEFT, 5);
    fgs->Add(hbox);

    vbox->Add(fgs, 0, wxALL, 10);

    this->circuitPanel = new CircuitPanel(this, wxID_ANY);
    vbox->Add(this->circuitPanel, 0, wxALL, 10);

    // OK/Cancel buttons
    wxSizer *buttonSizer = CreateSeparatedButtonSizer(wxOK | wxCANCEL);
    vbox->Add(buttonSizer, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 12);

    vbox->SetSizeHints(this);
    SetSizer(vbox);
  }

  unsigned int CsvImportDialog::getTemperatureColumn() const
  {
    return (unsigned int)this->spnTemperatureColumn->GetValue();
  }

  unsigned int CsvImportDialog::getOtherColumn() const
  {
    return (unsigned int)this->spnOtherColumn->GetValue();
  }
  
  ThermistorTableRow::Column CsvImportDialog::getOtherColumnType() const
  {
    const wxString value = this->comOtherColumnType->GetValue();
    if (value.IsSameAs(wxT("Resistance"))) {
      return ThermistorTableRow::RESISTANCE;
    } else if (value.IsSameAs(wxT("Voltage"))) {
      return ThermistorTableRow::VOLTAGE;
    } else if (value.IsSameAs(wxT("ADC value"))) {
      return ThermistorTableRow::ADCVALUE;
    } else {
      throw std::runtime_error("Invalid other column type selection.");
    }
  }

  shared_ptr<TemperatureScale> CsvImportDialog::getTemperatureScale() const
  {
    const wxString value = this->comTemperatureScale->GetValue();
    if (value.IsSameAs(wxT("Celsius"))) {
      return make_shared<CelsiusScale>();
    } else if (value.IsSameAs(wxT("Fahrenheit"))) {
      return make_shared<FahrenheitScale>();
    } else if (value.IsSameAs(wxT("Kelvin"))) {
      return make_shared<KelvinScale>();
    } else {
      throw std::runtime_error("Invalid temperature scale selection.");
    }
  }
  
  unsigned int CsvImportDialog::getNbLinesToSkip() const
  {
    return (unsigned int)this->spnNbLinesToSkip->GetValue();
  }
  
  char CsvImportDialog::getSeparator() const
  {
    const wxString value = this->comSeparator->GetValue();
    if (value.IsSameAs(wxT("Comma"))) {
      return ',';
    } else if (value.IsSameAs(wxT("Dot"))) {
      return '.';
    } else if (value.IsSameAs(wxT("Space"))) {
      return ' ';
    } else if (value.IsSameAs(wxT("Tab"))) {
      return '\t';
    } else if (value.Length() == 0) {
      return value.c_str()[0];
    } else {
      throw std::invalid_argument("Invalid separator character specified.");
    }
  }

  void CsvImportDialog::onOkButtonClicked(wxCommandEvent& event)
  {
    try {
      // TODO: input validation
    } catch (std::exception& e) {
      // TODO: show nice dialog
      std::cout << "Error: " << e.what() << std::endl;
    }
  }

  shared_ptr<ThermistorTable> CsvImportDialog::createThermistorTable()
  {
    wxFileDialog openFileDialog(this, _("Import CSV file"), wxEmptyString, 
				wxEmptyString, _("All Files|*.*;CSV files (*.csv)|*.csv;Text files (*.txt)|*.txt"), wxFD_OPEN|wxFD_FILE_MUST_EXIST);

    shared_ptr<ThermistorTable> result;
    if (openFileDialog.ShowModal() == wxID_OK) {
      std::string filename = std::string(openFileDialog.GetPath().mb_str());
      std::ifstream ifs(filename.c_str());

      const shared_ptr<Circuit> circuit = this->circuitPanel->createCircuit();
      const shared_ptr<AnalogToDigitalConverter> adc = this->circuitPanel->createAnalogToDigitalConverter();
      CsvThermistorTableReader reader(getTemperatureColumn(), getOtherColumn(), getOtherColumnType(), getTemperatureScale(), circuit, adc, getNbLinesToSkip(), getSeparator());

      result = reader.read(ifs);
    }

    return result;
  }

  unsigned int CsvImportDialog::getValueAsUnsignedInt(const wxString& string) const
  {
    long value = 0;
    string.ToLong(&value);
    return (unsigned int)value;
  }

}


